﻿using System;
using System.Collections.Generic;
using System.Reflection;
using System.Text;

namespace MatchingGame
{
    [Obfuscation(Feature = "instrumentation", Exclude = true, ApplyToMembers = true)]
    public class Tracer 
    {
        public static readonly Tracer Instance = new Tracer();
        private Tracer()
        {
        }

        [Obfuscation(Feature = "instrumentation on entry method")]
        public static void OnEntry(object instance, MethodBase method, params object[] args)
        {
            Tracer.Instance.OnMethodEntry(instance, method, args);
        }
        
        [Obfuscation(Feature = "instrumentation on exception method")]
        public static bool OnException(object instance, MethodBase method, Exception e)
        {
            return Tracer.Instance.OnMethodException(instance, method, e);
        }

        [Obfuscation(Feature = "instrumentation on exit method")]
        public static void OnExit(object instance, MethodBase method)
        {
            Tracer.Instance.OnMethodExit(instance, method);
        }

        private void OnMethodEntry(object instance, MethodBase method, params object[] args)
        {
            System.Diagnostics.Trace.WriteLine(String.Format("{0}::{1}",
            method.DeclaringType.FullName, BuildMethodSignature(method, args)));
            System.Diagnostics.Trace.Indent();
        }
        private bool OnMethodException(object instance, MethodBase method, Exception e)
        {
            // Note that the Exception e parameter can be null // in case the catch type has not been specified System.Diagnostics.Trace.TraceError("Method {0} throws an exception: {1}", method.Name, e);
            // true = Rethrow the exception
            return true;
        }

        private void OnMethodExit(object instance, MethodBase method)
        {
            System.Diagnostics.Trace.Unindent();
        }

        private string BuildMethodSignature(MethodBase method, object[] args)
        {
            StringBuilder sb = new StringBuilder(); 
            sb.Append(method.Name); sb.Append('('); 
            ParameterInfo[] parameters = method.GetParameters(); 
            for (int i = 0; i < parameters.Length; i++) 
            { 
                if (i > 0) sb.Append(", "); 
                ParameterInfo paramInfo = parameters[i]; 
                sb.Append(paramInfo.ParameterType.FullName); 
                try 
                { 
                    if (i < args.Length) 
                    { 
                        object arg = args[i]; sb.AppendFormat("={0}", arg == null ? "null" : arg.ToString()); 
                    } 
                } 
                catch (Exception e) { 
                    System.Diagnostics.Trace.TraceError(e.ToString()); 
                    System.Diagnostics.Trace.TraceError(String.Format("Parameter {0}={1}", i, paramInfo.ParameterType.FullName)); 
                } 
            }            
            sb.Append(')'); 
            return sb.ToString();
         }
    }
}
